package com.hwy.chart

import android.content.Context
import android.graphics.Canvas
import android.graphics.PointF
import android.graphics.RectF
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.widget.FrameLayout
import androidx.annotation.MainThread
import com.hwy.chart.component.BaseComponent
import com.hwy.chart.coordinate.BaseCoordinate
import com.hwy.chart.coordinate.SimpleCoordinate
import com.hwy.chart.coordinate.axis.ChartXAxis
import com.hwy.chart.coordinate.axis.ChartYAxis
import com.hwy.chart.cursor.BaseCursor
import com.hwy.chart.cursor.SimpleCursor
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/**
 * =======================================
 * 作者: hewenyu
 * 日期: 2022/5/10 15:23
 * 说明: 图表控件基类
 * =======================================
 */
abstract class BaseChartView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    /**
     * 记录当前图表的所有[BaseComponent]组件
     */
    protected val componentMap by lazy {
        HashMap<Any, BaseComponent<*>>()
    }

    /**
     * 图表坐标组件
     *
     * 默认x轴数据类型为 String
     * 默认y轴数据类型为 Float
     */
    var coordinate: BaseCoordinate<*, *> = SimpleCoordinate<String, Float>()

    /**
     * 光标组件
     */
    var cursor: BaseCursor = SimpleCursor(this)

    /**
     * 图表绘制区域
     * 可绘制区域 = 控件的尺寸 - 四个边界的 padding 值
     */
    private val graphRegion by lazy {
        RectF()
    }

    /**
     * 承载图表 legend 的容器
     */
    private lateinit var legendContainer: FrameLayout

    /**
     * 承载 emptyView 的容器
     */
    private lateinit var emptyContainer: FrameLayout

    /**
     * 记录当前光标的坐标点
     *
     * [cursorPoint.x]: 光标的x坐标
     * [cursorPoint.y]: 光标的y坐标
     */
    private val cursorPoint by lazy {
        PointF()
    }

    private val touchSlop by lazy {
        ViewConfiguration.get(context).scaledTouchSlop
    }

    /**
     * 标记当前touch事件序列是否是横向的
     * true: 横向时间序列，当前不能被父容器拦截
     * false: 纵向时间序列，当前能够父容器拦截
     */
    private var isHorizontalEvent = false

    /**
     * 记录down事件的坐标
     */
    private var startX = 0F
    private var startY = 0F

    override fun onFinishInflate() {
        super.onFinishInflate()
        initLegendContainer()
        initEmptyContainer()
        // 默认需要处理touch事件
        isClickable = true
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        cursor?.run {
            // legendContainer 容器layout
            if (legendContainer.parent == this@BaseChartView && isCursorEnable) {
                layoutLegendContainer(left, top, right, bottom)
            } else {
                legendContainer.layout(0, 0, 0, 0)
            }
        }
        // emptyContainer 容器layout
        layoutEmptyContainer(left, top, right, bottom)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 更新绘制区域
        updateGraphRegion(w, h)
    }

    /**
     * 更新图表的可绘制区域
     */
    private fun updateGraphRegion(w: Int, h: Int) {
        graphRegion.set(
            paddingLeft.toFloat(),
            paddingTop.toFloat(),
            w - paddingRight.toFloat(),
            h - paddingBottom.toFloat()
        )
    }

    /**
     * 获取x坐标轴
     */
    fun <T> getXAxis(): ChartXAxis<T> {
        return coordinate.xAxis as ChartXAxis<T>
    }

    /**
     * 获取与坐标轴
     */
    fun <T> getYAxis(): ChartYAxis<T> where T : Number, T : Comparable<T> {
        return coordinate.yAxis as ChartYAxis<T>
    }

    // region --------------------------------- 触摸事件相关函数 ---------------------------------

    /**
     * 触摸事件开始
     */
    protected open fun doTouchDown(event: MotionEvent) {
        isHorizontalEvent = false
        parent.requestDisallowInterceptTouchEvent(true)
        startX = event.x
        startY = event.y
    }

    /**
     * 触摸事件更新
     */
    protected open fun doTouchMove(event: MotionEvent) {
        if (isHorizontalEvent) {
            updateCursor(event)
        } else {
            val distanceX = abs(event.x - startX)
            val distanceY = abs(event.y - startY)
            if (distanceX >= touchSlop && distanceY < touchSlop) {
                isHorizontalEvent = true
            }
            if (distanceY >= touchSlop && distanceX < touchSlop) {
                // 纵向事件序列，可以被拦截
                requestDisallowInterceptTouchEvent(false)
            }
            updateCursor(event)
        }
    }

    /**
     * 更新光标信息
     */
    protected open fun updateCursor(event: MotionEvent) {
        cursor?.run {
            // 标记光标可见
            isCursorEnable = true
            // 校准坐标
            var cursorX = coordinate.xAxis.adjustCoordinate(event.x, graphRegion)
            cursorX = checkXAxisOffset(cursorX)
            // 更新数据
            val tagY = coordinate.yAxis.getDataByCoordinate(y, graphRegion)
            val tagX = coordinate.xAxis.getDataByCoordinate(cursorX, graphRegion)
            cursorPoint.set(cursorX ?: BaseCursor.INVALID_COORDINATE, event.y)
            updateCursorTag("${tagX ?: ""}", "${tagY ?: ""}")
            // 更新光标坐标
            updateCoordinate(
                cursorPoint.x,
                event.y,
                coordinate.xAxis.getIndexByCoordinate(cursorX, graphRegion)
            )

        }
        requestLayout()
        invalidate()
    }

    /**
     * 校验touch事件的x坐标是否落在图表绘制的区域
     */
    protected open fun checkXAxisOffset(cursorX: Float?): Float? {
        var value = cursorX
        value?.run {
            coordinate.xAxis.run {
                getIndexByCoordinate(value, graphRegion)?.let { touchIndex ->
                    if (touchIndex > dataOffset - 1) {
                        value = getCoordinateByIndex(dataOffset - 1, graphRegion)
                    }
                }
            }
        }
        return value
    }

    /**
     * 触摸事件结束
     */
    protected open fun doTouchEnd(event: MotionEvent) {
        // up事件时，需要标记光标隐藏
        cursor.run {
            isCursorEnable = false
            clearCoordinate()
        }
        cursorPoint.set(0F, 0F)
        requestLayout()
        invalidate()
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        event?.run {
            when (actionMasked) {
                MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> doTouchEnd(this)
                MotionEvent.ACTION_DOWN -> doTouchDown(this)
                MotionEvent.ACTION_MOVE -> doTouchMove(this)
            }
        }
        return super.onTouchEvent(event)
    }


    // endregion ------------------------------------------------------------------------------

    // region --------------------------------- 图表绘制相关 ---------------------------------

    override fun dispatchDraw(canvas: Canvas) {
        beforeDraw(canvas)
        drawChart(canvas)
        super.dispatchDraw(canvas)
        afterDraw(canvas)
    }

    /**
     * 绘制之前提供一个切入的函数
     */
    protected open fun beforeDraw(canvas: Canvas) {
        componentMap.forEach { entry ->
            entry.value.let {
                if (it.isShow) {
                    it.beforeDraw(canvas, graphRegion)
                }
            }
        }
    }

    /**
     * 绘制图表
     */
    protected open fun drawChart(canvas: Canvas) {
        // 绘制坐标系
        coordinate.draw(canvas, graphRegion)
        // 绘制组件
        drawComponent(canvas, graphRegion)
        // 绘制光标
        cursor.draw(canvas, graphRegion)
    }

    /**
     * 绘制组件列表
     */
    protected open fun drawComponent(canvas: Canvas, bounds: RectF) {
        componentMap.forEach { entry ->
            entry.value.let {
                if (it.isShow) {
                    it.draw(canvas, bounds)
                }
            }
        }
    }

    /**
     * 在绘制结束之后提供一个切入的函数
     */
    protected open fun afterDraw(canvas: Canvas) {
        componentMap.forEach { entry ->
            entry.value.let {
                if (it.isShow) {
                    it.afterDraw(canvas, graphRegion)
                }
            }
        }
    }

    // endregion --------------------------------------------------------------------------

    // region --------------------------------- emptyView相关函数 ---------------------------------

    /**
     * 初始化 emptyView 容器
     */
    private fun initEmptyContainer() {
        emptyContainer = FrameLayout(context)
        addView(emptyContainer, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
    }

    protected open fun layoutEmptyContainer(left: Int, top: Int, right: Int, bottom: Int) {
        if (calculateShowComponentCount() <= 0) {
            emptyContainer.layout(
                paddingLeft,
                paddingTop,
                paddingLeft + emptyContainer.measuredWidth,
                paddingTop + emptyContainer.measuredHeight
            )
        } else {
            emptyContainer.layout(0, 0, 0, 0)
        }
    }

    /**
     * 添加 EmptyView
     */
    @JvmOverloads
    fun addEmptyView(view: View?, layoutParams: LayoutParams? = null) {
        emptyContainer.removeAllViews()
        view?.run {
            emptyContainer.addView(
                this,
                layoutParams ?: LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
            )
        }
    }

    /**
     * 计算显示的组件数量
     */
    protected open fun calculateShowComponentCount(): Int {
        var count = 0
        componentMap.forEach { entry ->
            if (entry.value.isDataValid()) {
                count++
            }
        }
        return count
    }

    // endregion --------------------------------------------------------------------------------

    // region --------------------------------- 图表legend相关函数 ---------------------------------

    /**
     * 初始化图表 legend 容器
     */
    private fun initLegendContainer() {
        legendContainer = FrameLayout(context)
        addView(legendContainer, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
    }

    /**
     * 在 [onLayout] 函数中自定义 图表legend 模块的位置
     */
    protected open fun layoutLegendContainer(left: Int, top: Int, right: Int, bottom: Int) {
        val left = getLegendLeft(cursorPoint, graphRegion).toInt()
        val top = getLegendTop(cursorPoint, graphRegion).toInt()
        val legendWidth = legendContainer.measuredWidth
        val legendHeight = legendContainer.measuredHeight
        legendContainer.layout(left, top, left + legendWidth, top + legendHeight)
    }

    /**
     * 计算图表legend显示的top位置
     *
     * [legendContainer] 显示的纵向位置 = top + [legendContainer] 测量高度
     */
    protected open fun getLegendTop(point: PointF, bounds: RectF): Float {
        return point.run {
            if (y > height / 2F) {
                // 光标在下方，legend显示在上方
                bounds.top
            } else {
                // 光标在上方，legend显示在下方
                bounds.bottom - legendContainer.measuredHeight
            }
        }
    }

    /**
     * 计算图表legend显示的left位置
     *
     * [legendContainer] 显示的横向位置 = left + [legendContainer] 测量宽度
     */
    protected open fun getLegendLeft(point: PointF, bounds: RectF): Float {
        return point.run {
            // 阈值判断
            val realX = max(min(x, bounds.right), bounds.left).toInt()
            if (realX < paddingLeft + bounds.width() / 2) {
                // 光标在左侧，legend显示在右侧
                bounds.right - legendContainer.measuredWidth
            } else {
                // 光标在右侧，legend显示在左侧
                bounds.left
            }
        }
    }

    /**
     * 添加legend视图
     */
    @JvmOverloads
    fun addLegendView(view: View?, layoutParams: LayoutParams? = null) {
        legendContainer.removeAllViews()
        view?.run {
            legendContainer.addView(
                this,
                layoutParams ?: LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
            )
        }
    }

    // endregion ---------------------------------------------------------------------------------

    // region --------------------------------- 图表组件操作 ---------------------------------

    /**
     * 根据Key查询对应的图表组件
     */
    @MainThread
    fun <T> getComponent(key: Any?): BaseComponent<T>? where T : Number, T : Comparable<T> {
        return key?.run {
            componentMap[key] as BaseComponent<T>?
        }
    }

    /**
     * 添加图表组件
     */
    @MainThread
    fun <T> addComponent(
        key: Any?,
        component: BaseComponent<T>?
    ) where T : Number, T : Comparable<T> {
        if (componentMap.containsKey(key)) {
            componentMap.remove(key)
        }
        key?.let {
            component?.run {
                componentMap[key] = this
                // 绑定坐标轴
                bindAxis(getYAxis())
                // 添加光标监听
                cursor.addCursorChangeListener(this)
            }
        }
    }

    /**
     * 移除图表组件
     */
    @MainThread
    fun <T> removeComponent(key: Any?) where T : Number, T : Comparable<T> {
        key?.run {
            if (componentMap.containsKey(this)) {
                getComponent<T>(this)?.run {
                    // 解绑组件和坐标轴
                    unBindAxis(getYAxis())
                    // 移除光标监听
                    cursor.removeCursorChangeListener(this)
                }
                // 从组件列表中移除
                componentMap.remove(this)
            }
        }
    }

    fun clearComponent() {
        coordinate.yAxis.clearComponent()
        componentMap.clear()
        requestLayout()
        invalidate()
    }

    // endregion ---------------------------------------------------------------------------

}